<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>part2_5_memoryobject - learning-gem5</title>
        <!-- Custom HTML head -->
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">
        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../highlight.css">
        <link rel="stylesheet" href="../tomorrow-night.css">
        <link rel="stylesheet" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->
    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="../part0_introduction.html">Learning gem-5</a></li><li class="chapter-item expanded "><a href="../part0_introduction.html"><strong aria-hidden="true">1.</strong> part0_introduction</a></li><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.</strong> part1</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.1.</strong> part1_1_building</a></li><li class="chapter-item expanded "><a href="../part1/part1_2_simple_config.html"><strong aria-hidden="true">2.2.</strong> part1_2_simple_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_3_cache_config.html"><strong aria-hidden="true">2.3.</strong> part1_3_cache_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_4_gem5_stats.html"><strong aria-hidden="true">2.4.</strong> part1_4_gem5_stats</a></li><li class="chapter-item expanded "><a href="../part1/part1_5_gem5_example_configs.html"><strong aria-hidden="true">2.5.</strong> part1_5_gem5_example_configs</a></li><li class="chapter-item expanded "><a href="../part1/part1_6_extending_configs.html"><strong aria-hidden="true">2.6.</strong> part1_6_extending_configs</a></li></ol></li><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.</strong> part2</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.1.</strong> part2_0_environment</a></li><li class="chapter-item expanded "><a href="../part2/part2_1_helloobject.html"><strong aria-hidden="true">3.2.</strong> part2_1_helloobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_2_debugging.html"><strong aria-hidden="true">3.3.</strong> part2_2_debugging</a></li><li class="chapter-item expanded "><a href="../part2/part2_3_events.html"><strong aria-hidden="true">3.4.</strong> part2_3_events</a></li><li class="chapter-item expanded "><a href="../part2/part2_4_parameters.html"><strong aria-hidden="true">3.5.</strong> part2_4_parameters</a></li><li class="chapter-item expanded "><a href="../part2/part2_5_memoryobject.html" class="active"><strong aria-hidden="true">3.6.</strong> part2_5_memoryobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_6_simplecache.html"><strong aria-hidden="true">3.7.</strong> part2_6_simplecache</a></li><li class="chapter-item expanded "><a href="../part2/part2_7_arm_power_modelling.html"><strong aria-hidden="true">3.8.</strong> part2_7_arm_power_modelling</a></li><li class="chapter-item expanded "><a href="../part2/part2_8_arm_dvfs_support.html"><strong aria-hidden="true">3.9.</strong> part2_8_arm_dvfs_support</a></li></ol></li><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.</strong> part3</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.1.</strong> part3_00_MSIntro</a></li><li class="chapter-item expanded "><a href="../part3/part3_01_cache-intro.html"><strong aria-hidden="true">4.2.</strong> part3_01_cache-intro</a></li><li class="chapter-item expanded "><a href="../part3/part3_02_cache-declarations.html"><strong aria-hidden="true">4.3.</strong> part3_02_cache-declarations</a></li><li class="chapter-item expanded "><a href="../part3/part3_03_cache-in-ports.html"><strong aria-hidden="true">4.4.</strong> part3_03_cache-in-ports</a></li><li class="chapter-item expanded "><a href="../part3/part3_04_cache_actions.html"><strong aria-hidden="true">4.5.</strong> part3_04_cache_actions</a></li><li class="chapter-item expanded "><a href="../part3/part3_05_cache_transitions.html"><strong aria-hidden="true">4.6.</strong> part3_05_cache_transitions</a></li><li class="chapter-item expanded "><a href="../part3/part3_06_directory.html"><strong aria-hidden="true">4.7.</strong> part3_06_directory</a></li><li class="chapter-item expanded "><a href="../part3/part3_07_MSIbuilding.html"><strong aria-hidden="true">4.8.</strong> part3_07_MSIbuilding</a></li><li class="chapter-item expanded "><a href="../part3/part3_08_configuration.html"><strong aria-hidden="true">4.9.</strong> part3_08_configuration</a></li><li class="chapter-item expanded "><a href="../part3/part3_09_running.html"><strong aria-hidden="true">4.10.</strong> part3_09_running</a></li><li class="chapter-item expanded "><a href="../part3/part3_10_MSIdebugging.html"><strong aria-hidden="true">4.11.</strong> part3_10_MSIdebugging</a></li><li class="chapter-item expanded "><a href="../part3/part3_11_simple-MI_example.html"><strong aria-hidden="true">4.12.</strong> part3_11_simple-MI_example</a></li></ol></li><li class="chapter-item expanded "><a href="../part4_gem5_101.html"><strong aria-hidden="true">5.</strong> part4_gem5_101</a></li><li class="chapter-item expanded "><a href="../http://doxygen.gem5.org/develop/index.html"><strong aria-hidden="true">6.</strong> part4_gem5_102</a></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">learning-gem5</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="在内存系统中创建-simobjects"><a class="header" href="#在内存系统中创建-simobjects">在内存系统中创建 SimObjects</a></h1>
<p>在本章中，我们将创建一个位于 CPU 和内存总线之间的简单内存对象。在<a href="https://www.gem5.org/documentation/learning_gem5/part2/simplecache">下一章中，</a> 我们将利用这个简单的内存对象并为其添加一些逻辑，使其成为一个非常简单的阻塞单处理器缓存。</p>
<h2 id="gem5-主从端口"><a class="header" href="#gem5-主从端口">gem5 主从端口</a></h2>
<p>在深入研究内存对象的实现之前，我们应该首先了解 gem5 的主从端口接口。正如之前在<a href="https://www.gem5.org/documentation/learning_gem5/part1/simple_config">simple-config-chapter 中</a>讨论的，所有内存对象都通过端口连接在一起。这些端口在这些内存对象之间提供了一个严格的接口。</p>
<p>这些端口实现了三种不同的内存系统<em>模式</em>：定时、原子和功能。最重要的模式是<em>计时模式</em>。计时模式是唯一能够产生正确仿真结果的模式。其他模式仅在特殊情况下使用。</p>
<p><em>原子模式</em>可用于预热模拟器并将模拟快进到关键区域。这种模式假设内存系统中不会产生任何事件。相反，所有内存请求都通过单个长调用链执行。一般不需要为内存对象实现原子访问，除非它将在快进或模拟器预热期间使用。</p>
<p><em>功能模式</em>也可以称为<em>调试模式</em>。功能模式用于将数据从主设备读取到模拟器内存中。它在系统调用仿真模式中大量使用。例如，功能模式用于<code>process.cmd</code>将主设备中的二进制文件加载 到模拟系统的内存中，以便模拟系统可以访问它。功能访问应该在读取时返回最新的数据，无论数据在哪里，并且应该在写入时更新所有可能的有效数据（例如，在具有缓存的系统中，可能有多个有效的缓存块与地址相同）。</p>
<h3 id="数据包packets"><a class="header" href="#数据包packets">数据包（Packets）</a></h3>
<p>在 gem5 中，<code>Packets</code>是跨端口发送的。一个<code>Packet</code>由一个内存请求对象<code>MemReq</code>组成。<code>MemReq</code>保存关于发起所述数据包如请求者，地址，和请求的类型（读，写等）的原始请求的信息。</p>
<p>数据包也有一个<code>MemCmd</code>，它是数据包的<em>当前</em>命令。这个命令可以在数据包的整个生命周期中改变（例如，一旦满足内存命令，请求就会变成响应）。最常见的<code>MemCmd</code>有<code>ReadReq</code>（读请求）、<code>ReadResp</code>（读响应）、<code>WriteReq</code>（写请求）、<code>WriteResp</code>（写响应）。还有针对缓存和许多其他命令类型的写回请求 ( <code>WritebackDirty</code>, <code>WritebackClean</code>)。</p>
<p>数据包也可以保留请求的数据，或指向数据的指针。无论数据是动态的（显式分配和解除分配）还是静态的（由数据包对象分配和解除分配），在创建数据包时都有一些选项。</p>
<p>最后，在经典缓存中使用数据包作为跟踪一致性的单元。因此，许多数据包代码特定于经典缓存一致性协议。然而，数据包用于 gem5 中内存对象之间的所有通信，即使它们不直接涉及一致性（例如，DRAM 控制器和 CPU 模型）。</p>
<p>所有端口接口函数都接受一个<code>Packet</code>指针作为参数。由于这个指针非常常见，gem5 为它包含了一个 typedef：<code>PacketPtr</code>.</p>
<h3 id="端口接口"><a class="header" href="#端口接口">端口接口</a></h3>
<p>gem5中有两种类型的端口：主端口和从端口。每当您实现一个内存对象时，您将至少实现这些类型的端口之一。为此，您需要创建一个新类，该类分别继承自<code>MasterPort</code>或<code>SlavePort</code>，也就是主端口或从端口。主端口发送请求（并接收响应），从端口接收请求（并发送响应）。</p>
<p>下图概述了主端口和从端口之间最简单的交互。该图显示了时序模式下的交互。其他模式要简单得多，并且在主设备和从设备之间使用简单的调用链。</p>
<p><img src="part2_5_memoryobject.assets/master_slave_1.png" alt="当两者都可以接受请求和响应时，简单的主从交互。" /></p>
<p>如上所述，所有端口接口都需要 一个<code>PacketPtr</code>作为参数。这些函数（<code>sendTimingReq</code>、<code>recvTimingReq</code>等）也都接受一个 <code>PacketPtr</code>。此数据包表示发送或接收的请求或响应。</p>
<p>要发送请求数据包，主设备调用<code>sendTimingReq</code>。从设备（在同一个调用链中）返回响应时，会调用<code>recvTimingReq</code>，且其中的<code>PacketPtr</code>参数和主设备传入的一致。</p>
<p><code>recvTimingReq</code>的返回类型为<code>bool</code>。这个布尔返回值直接返回给调用者。返回值 <code>true</code>表示数据包已被从设备接受。返回<code>false</code>意味着从设备无法接受并且必须在将来的某个时间重试该请求。</p>
<p>在上图，首先，主设备通过调用发送定时请求<code>sendTimingReq</code>，其响应由<code>recvTimingResp</code>产生。从设备通过<code>recvTimingResp</code>返回true，作为<code>sendTimingReq</code>的返回值。主设备继续执行其他任务，从设备异步完成请求（例如，如果它是一个缓存，它会查找标签以查看是否与请求中的地址匹配）。</p>
<p>一旦从设备完成请求，它就可以向主设备发送响应。从设备调用<code>sendTimingResp</code>响应数据包（传入的<code>PacketPtr</code>与请求时相同，但现在是响应数据包），使主设备<code>recvTimingResp</code>被调用。主设备的<code>recvTimingResp</code>返回<code>true</code>，即从设备的<code>sendTimingResp</code>中的返回值。这样，该请求的交互就完成了。</p>
<p>稍后在 master-slave-example-section 中，我们将展示这些函数的示例代码。</p>
<p>主设备或从设备在收到请求或响应时可能正忙。下图显示了原始请求发送时从设备忙的情况。</p>
<p><img src="part2_5_memoryobject.assets/master_slave_2.png" alt="从设备忙时简单的主从交互" /></p>
<p>在这种情况下，从设备通过<code>recvTimingReq</code> 函数返回<code>false</code>。当主设备在调用<code>sendTimingReq</code>后收到false时，它必须等到<code>recvReqRetry</code>被调用时才能重试 <code>sendTimingReq</code>。上图显示了计时请求失败一次，但它可能失败任意多次。注意：跟踪失败的数据包是由主设备负责，而不是从设备。从设备<em>不</em>保留指向失败数据包的指针。</p>
<p>类似地，该图显示了当从设备尝试发送响应时主设备忙的情况。在这种情况下，从设备在被调用<code>recvRespRetry</code>前无法再次调用<code>sendTimingResp</code>.</p>
<p><img src="part2_5_memoryobject.assets/master_slave_3.png" alt="主忙时简单的主从交互" /></p>
<p>需要注意的是，在这两种情况下，重试代码路径可以是单个调用堆栈。例如，当主设备调用<code>sendRespRetry</code>时， <code>recvTimingReq</code>也可以在同一个调用栈中调用。因此，很容易错误地创建无限递归错误或其他错误。因此，确保一个内存对象发送重试请求之前，它已准备好<em>在那一瞬间</em>接受另一个包非常重要。</p>
<h2 id="简单的内存对象示例"><a class="header" href="#简单的内存对象示例">简单的内存对象示例</a></h2>
<p>在本节中，我们将构建一个简单的内存对象。最初，它只会将请求从 CPU 端（一个简单的 CPU）传递到内存端（一个简单的内存总线）。见下图。它将有一个主端口，用于向内存总线发送请求，以及两个 CPU 侧端口，用于 CPU 的指令和数据缓存端口。在下一章<a href="https://www.gem5.org/documentation/learning_gem5/part2/simplecache">simplecache-chapter 中</a>，我们将添加使该对象成为缓存的逻辑。</p>
<p><img src="part2_5_memoryobject.assets/simple_memobj.png" alt="具有位于 CPU 和内存总线之间的简单内存对象的系统。" /></p>
<h3 id="声明-simobject"><a class="header" href="#声明-simobject">声明 SimObject</a></h3>
<p>正如我们在<a href="https://www.gem5.org/documentation/learning_gem5/part2/helloobject">hello-simobject-chapter</a>中创建简单 <a href="https://www.gem5.org/documentation/learning_gem5/part2/helloobject">的 SimObject 一样</a>，第一步是创建一个 SimObject Python 文件。我们将调用这个简单的内存对象<code>SimpleMemobj</code>并在<code>src/learning_gem5/simple_memobj</code>.</p>
<pre><code class="language-python">from m5.params import *
from m5.proxy import *
from m5.SimObject import SimObject

class SimpleMemobj(SimObject):
    type = 'SimpleMemobj'
    cxx_header = &quot;learning_gem5/part2/simple_memobj.hh&quot;

    inst_port = SlavePort(&quot;CPU side port, receives requests&quot;)
    data_port = SlavePort(&quot;CPU side port, receives requests&quot;)
    mem_side = MasterPort(&quot;Memory side port, sends requests&quot;)
</code></pre>
<p>我们让这个对象继承自<code>SimObject</code>。 <code>SimObject</code>类有一个纯虚函数<code>getPort</code>需要我们在C ++代码中实现。</p>
<p>这个对象的参数是三个端口。其中两个是连接CPU 的指令端口和数据端口，第三个端口连接内存总线。这些端口没有默认值，并且有简单的描述。</p>
<p>记住这些端口的名称很重要。我们将在实现<code>SimpleMemobj</code>和定义 <code>getPort</code>函数时明确使用这些名称。</p>
<p>您可以在<a href="https://www.gem5.org/_pages/static/scripts/part2/memoryobject/SimpleMemobj.py">此处</a>下载 SimObject 文件 。</p>
<p>当然，您还需要在新目录中创建一个 SConscript 文件来声明 SimObject Python 文件。您可以在<a href="https://www.gem5.org/_pages/static/scripts/part2/memoryobject/SConscript">此处</a>下载 SConscript 文件 。</p>
<h3 id="定义-simplememobj-类"><a class="header" href="#定义-simplememobj-类">定义 SimpleMemobj 类</a></h3>
<p>现在，我们为<code>SimpleMemobj</code>.</p>
<pre><code class="language-cpp">#include &quot;mem/port.hh&quot;
#include &quot;params/SimpleMemobj.hh&quot;
#include &quot;sim/sim_object.hh&quot;

class SimpleMemobj : public SimObject
{
  private:

  public:

    /** constructor
     */
    SimpleMemobj(SimpleMemobjParams *params);
};
</code></pre>
<h3 id="定义从端口类型"><a class="header" href="#定义从端口类型">定义从端口类型</a></h3>
<p>现在，我们需要为我们的两种端口定义类：CPU 端和内存端端口。为此，我们将在<code>SimpleMemobj</code>类中声明这些类，因为没有其他对象会使用这些类。</p>
<p>让我们从从端口开始，或者说 CPU 端端口。我们将从<code>SlavePort</code>类继承。以下是重写<code>SlavePort</code>类中所有纯虚函数所需的代码。</p>
<pre><code class="language-cpp">class CPUSidePort : public SlavePort
{
  private:
    SimpleMemobj *owner;

  public:
    CPUSidePort(const std::string&amp; name, SimpleMemobj *owner) :
        SlavePort(name, owner), owner(owner)
    { }

    AddrRangeList getAddrRanges() const override;

  protected:
    Tick recvAtomic(PacketPtr pkt) override { panic(&quot;recvAtomic unimpl.&quot;); }
    void recvFunctional(PacketPtr pkt) override;
    bool recvTimingReq(PacketPtr pkt) override;
    void recvRespRetry() override;
};
</code></pre>
<p>这个对象需要定义五个函数。</p>
<p>该对象还有一个成员变量，即它的所有者，因此它可以调用该对象上的函数。</p>
<h3 id="定义主端口类型"><a class="header" href="#定义主端口类型">定义主端口类型</a></h3>
<p>接下来，我们需要定义主端口类型。这将是内存端端口，它将请求从 CPU 端转发到内存系统的其余部分。</p>
<pre><code class="language-cpp">class MemSidePort : public MasterPort
{
  private:
    SimpleMemobj *owner;

  public:
    MemSidePort(const std::string&amp; name, SimpleMemobj *owner) :
        MasterPort(name, owner), owner(owner)
    { }

  protected:
    bool recvTimingResp(PacketPtr pkt) override;
    void recvReqRetry() override;
    void recvRangeChange() override;
};
</code></pre>
<p>这个类只有三个我们必须重写的纯虚函数。</p>
<h3 id="定义-simobject-接口"><a class="header" href="#定义-simobject-接口">定义 SimObject 接口</a></h3>
<p>既然我们已经定义了<code>CPUSidePort</code>类和 <code>MemSidePort</code>类，我们可以将我们的三个端口声明为<code>SimpleMemobj</code>的成员变量. 我们还需要在<code>SimObject</code>类中声明纯虚函数 <code>getPort</code>。gem5 在初始化阶段使用该函数通过端口将内存对象连接在一起。</p>
<pre><code class="language-cpp">class SimpleMemobj : public SimObject
{
  private:

    &lt;CPUSidePort 声明&gt;
    &lt;MemSidePort 声明&gt;

    CPUSidePort instPort;
    CPUSidePort dataPort;

    MemSidePort memPort;

  public:
    SimpleMemobj(SimpleMemobjParams *params);

    Port &amp;getPort(const std::string &amp;if_name,
                  PortID idx=InvalidPortID) override;
};
</code></pre>
<p>您可以在<code>SimpleMemobj</code> <a href="https://www.gem5.org/_pages/static/scripts/part2/memoryobject/simple_memobj.hh">此处</a>下载头文件。</p>
<h3 id="实现基本的-simobject-函数"><a class="header" href="#实现基本的-simobject-函数">实现基本的 SimObject 函数</a></h3>
<p>我们将在<code>SimpleMemobj</code>的构造函数中简单地调用 <code>SimObject</code>的构造函数。我们还需要初始化所有端口。每个端口的构造函数都有两个参数：名称和指向其所有者的指针，正如我们在头文件中定义的那样。该名称可以是任何字符串，但按照惯例，它与 Python SimObject 文件中的名称相同。我们还将blocked 初始化为false。</p>
<pre><code class="language-cpp">#include &quot;learning_gem5/part2/simple_memobj.hh&quot;
#include &quot;debug/SimpleMemobj.hh&quot;

SimpleMemobj::SimpleMemobj(SimpleMemobjParams *params) :
    SimObject(params),
    instPort(params-&gt;name + &quot;.inst_port&quot;, this),
    dataPort(params-&gt;name + &quot;.data_port&quot;, this),
    memPort(params-&gt;name + &quot;.mem_side&quot;, this), blocked(false)
{
}
</code></pre>
<p>接下来，我们需要实现接口以获取端口。这个接口由函数<code>getPort</code>组成，该函数有两个参数。<code>if_name</code>(interface_name)是<em>此</em>对象中的接口在Python中的变量名。</p>
<p>为了实现<code>getPort</code>，我们比较<code>if_name</code>并判断它是不是<code>&quot;mem_side&quot;</code>，如我们在 Python SimObject 文件中指定的那样。如果是，那么我们返回<code>memPort</code>对象。如果名称为<code>&quot;inst_port&quot;</code>，则返回 instPort，如果名称为，<code>data_port</code>则返回dataPort。如果都不是，那么我们将请求名称传递给父级。</p>
<pre><code class="language-cpp">Port &amp;
SimpleMemobj::getPort(const std::string &amp;if_name, PortID idx)
{
    panic_if(idx != InvalidPortID, &quot;This object doesn't support vector ports&quot;);

    // This is the name from the Python SimObject declaration (SimpleMemobj.py)
    if (if_name == &quot;mem_side&quot;) {
        return memPort;
    } else if (if_name == &quot;inst_port&quot;) {
        return instPort;
    } else if (if_name == &quot;data_port&quot;) {
        return dataPort;
    } else {
        // pass it along to our super class
        return SimObject::getPort(if_name, idx);
    }
}
</code></pre>
<h3 id="实现从端口和主端口功能"><a class="header" href="#实现从端口和主端口功能">实现从端口和主端口功能</a></h3>
<p>从端口和主端口的实现都比较简单。大多数情况下，每个端口函数只是将信息转发到主内存对象 ( <code>SimpleMemobj</code>)。</p>
<p>从两个简单的函数开始，简单调用owner(<code>SimpleMemobj</code>子类对象)中对应方法的<code>getAddrRanges</code>和<code>recvFunctional</code> 。</p>
<pre><code class="language-cpp">AddrRangeList
SimpleMemobj::CPUSidePort::getAddrRanges() const
{
    return owner-&gt;getAddrRanges();
}

void
SimpleMemobj::CPUSidePort::recvFunctional(PacketPtr pkt)
{
    return owner-&gt;handleFunctional(pkt);
}
</code></pre>
<p>这些函数在 中的<code>SimpleMemobj</code>实现同样简单。这些实现只是将请求传递到内存端。我们也可以<code>DPRINTF</code>在此处使用调用来跟踪正在发生的情况以进行调试。</p>
<pre><code class="language-cpp">void
SimpleMemobj::handleFunctional(PacketPtr pkt)
{
    memPort.sendFunctional(pkt);
}

AddrRangeList
SimpleMemobj::getAddrRanges() const
{
    DPRINTF(SimpleMemobj, &quot;Sending new ranges\n&quot;);
    return memPort.getAddrRanges();
}
</code></pre>
<p>类似地，对于<code>MemSidePort</code>，我们需要实现<code>recvRangeChange</code> 并通过<code>SimpleMemobj</code>将请求转发到从端口。</p>
<pre><code class="language-cpp">void
SimpleMemobj::MemSidePort::recvRangeChange()
{
    owner-&gt;sendRangeChange();
}
void
SimpleMemobj::sendRangeChange()
{
    instPort.sendRangeChange();
    dataPort.sendRangeChange();
}
</code></pre>
<h3 id="实现接收请求"><a class="header" href="#实现接收请求">实现接收请求</a></h3>
<p><code>recvTimingReq</code>的实现稍微复杂一些。我们需要检查<code>SimpleMemobj</code>是否可以接受请求。 <code>SimpleMemobj</code>是一个非常简单的阻塞结构；我们一次只允许一个未完成的请求。因此，如果我们收到一个请求而当前请求未完成，<code>SimpleMemobj</code>则将阻塞第二个请求。</p>
<p>为了简化实现，<code>CPUSidePort</code>存储了端口接口的所有流量控制信息。因此，我们需要添加一个额外的成员变量 ，bool <code>needRetry</code>到<code>CPUSidePort</code>，用于存储我们是否需要在<code>SimpleMemobj</code> 空闲时发送重试。然后，如果<code>SimpleMemobj</code>请求被阻止，我们设置我们需要在未来某个时间发送重试。</p>
<pre><code class="language-cpp">bool
SimpleMemobj::CPUSidePort::recvTimingReq(PacketPtr pkt)
{
    if (!owner-&gt;handleRequest(pkt)) {
        needRetry = true;
        return false;
    } else {
        return true;
    }
}
</code></pre>
<p>为了处理对<code>SimpleMemobj</code>的请求，我们首先检查 <code>SimpleMemobj</code>是否已经因等待对另一个请求的响应阻塞。如果它被阻塞，那么我们返回<code>false</code>通知调用主端口我们现在不能接受请求。否则，我们将端口标记为阻塞并将数据包从内存端口发送出去。为此，我们可以在<code>MemSidePort</code>对象中定义一个辅助函数来隐藏<code>SimpleMemobj</code>实现中的流控制。我们将假设<code>memPort</code>处理所有的流控制并且总是在我们成功消费请求后从<code>handleRequest</code>返回 <code>true</code>。</p>
<pre><code class="language-cpp">bool
SimpleMemobj::handleRequest(PacketPtr pkt)
{
    if (blocked) {
        return false;
    }
    DPRINTF(SimpleMemobj, &quot;Got request for addr %#x\n&quot;, pkt-&gt;getAddr());
    blocked = true;
    memPort.sendPacket(pkt);
    return true;
}
</code></pre>
<p>接下来，我们需要在 <code>MemSidePort</code>实现<code>sendPacket</code>的功能。该函数将处理流量控制，以防其对等从端口不能接受请求。为此，我们需要向<code>MemSidePort</code>中添加一个成员来存储数据包，以防它被阻塞。如果接收方无法收到请求（或响应），则发送方有责任存储数据包。</p>
<p>这个函数只是通过调用函数<code>sendTimingReq</code>来发送数据包。如果发送失败，则此对象将数据包存储在<code>blockedPacket</code>成员函数中，以便稍后（当它收到<code>recvReqRetry</code>时）发送数据包。此函数还包含一些防御性代码，以确保没有错误，并且我们永远不会尝试错误地覆盖<code>blockedPacket</code>变量。</p>
<pre><code class="language-cpp">void
SimpleMemobj::MemSidePort::sendPacket(PacketPtr pkt)
{
    panic_if(blockedPacket != nullptr, &quot;Should never try to send if blocked!&quot;);
    if (!sendTimingReq(pkt)) {
        blockedPacket = pkt;
    }
}
</code></pre>
<p>接下来，我们需要实现重新发送数据包的代码。在这个函数中，我们尝试通过调用我们上面写的<code>sendPacket</code>函数来重新发送数据包。</p>
<pre><code class="language-cpp">void
SimpleMemobj::MemSidePort::recvReqRetry()
{
    assert(blockedPacket != nullptr);

    PacketPtr pkt = blockedPacket;
    blockedPacket = nullptr;

    sendPacket(pkt);
}
</code></pre>
<h3 id="实现接收响应"><a class="header" href="#实现接收响应">实现接收响应</a></h3>
<p>响应代码路径类似于接收代码路径。当 <code>MemSidePort</code>得到响应时，我们将响应通过<code>SimpleMemobj</code>转发到适当的<code>CPUSidePort</code>。</p>
<pre><code class="language-cpp">bool
SimpleMemobj::MemSidePort::recvTimingResp(PacketPtr pkt)
{
    return owner-&gt;handleResponse(pkt);
}
</code></pre>
<p>在<code>SimpleMemobj</code>中，当我们收到响应后，它应当总是进入阻塞状态，因为对象是阻塞的。在将数据包发送回 CPU 端之前，我们需要标记该对象不再被阻塞。这必须*在调用之前<code>sendTimingResp</code>*完成。否则，可能会陷入无限循环，因为主端口在接收响应和发送另一个请求之间可能只有一个调用链。</p>
<p>解除<code>SimpleMemobj</code>的阻塞后，我们检查包是指令还是数据，然后通过适当的端口将其发送回。最后，由于对象现在已解除阻塞，我们可能需要通知 CPU 端端口重试失败的请求。</p>
<pre><code class="language-cpp">bool
SimpleMemobj::handleResponse(PacketPtr pkt)
{
    assert(blocked);
    DPRINTF(SimpleMemobj, &quot;Got response for addr %#x\n&quot;, pkt-&gt;getAddr());

    blocked = false;

    // Simply forward to the memory port
    if (pkt-&gt;req-&gt;isInstFetch()) {
        instPort.sendPacket(pkt);
    } else {
        dataPort.sendPacket(pkt);
    }

    instPort.trySendRetry();
    dataPort.trySendRetry();

    return true;
}
</code></pre>
<p>类似于我们在<code>MemSidePort</code>中实现发送数据包的便利功能，我们可以在<code>CPUSidePort</code>中实现一个<code>sendPacket</code>功能， 将响应发送到 CPU 端。此函数调用 <code>sendTimingResp</code>，它将调用对等主端口的<code>recvTimingResp</code>。如果这个调用失败并且对等端口当前被阻塞，那么我们存储稍后发送的数据包。</p>
<pre><code class="language-cpp">void
SimpleMemobj::CPUSidePort::sendPacket(PacketPtr pkt)
{
    panic_if(blockedPacket != nullptr, &quot;Should never try to send if blocked!&quot;);

    if (!sendTimingResp(pkt)) {
        blockedPacket = pkt;
    }
}
</code></pre>
<p>我们将在收到 <code>recvRespRetry</code>后发送被阻塞的数据包。这个函数上面的完全一样，只是尝试重新发送数据包，这可能会再次被阻塞。</p>
<pre><code class="language-cpp">void
SimpleMemobj::CPUSidePort::recvRespRetry()
{
    assert(blockedPacket != nullptr);

    PacketPtr pkt = blockedPacket;
    blockedPacket = nullptr;

    sendPacket(pkt);
}
</code></pre>
<p>最后，我们需要为 <code>CPUSidePort</code>实现<code>trySendRetry</code>。这个函数由 <code>SimpleMemobj</code>在自身可能解除阻塞时调用。<code>trySendRetry</code>检查是否需要重试，如果需要重试，该函数调用<code>sendRetryReq</code>，然后调用 对等主端口（在本例中为 CPU）的<code>recvReqRetry</code>。</p>
<pre><code class="language-cpp">void
SimpleMemobj::CPUSidePort::trySendRetry()
{
    if (needRetry &amp;&amp; blockedPacket == nullptr) {
        needRetry = false;
        DPRINTF(SimpleMemobj, &quot;Sending retry req for %d\n&quot;, id);
        sendRetryReq();
    }
}
</code></pre>
<p>除了这个函数之外，为了完成这个文件，添加 SimpleMemobj 的 create 函数。</p>
<pre><code class="language-cpp">SimpleMemobj*
SimpleMemobjParams::create()
{
    return new SimpleMemobj(this);
}
</code></pre>
<p>您可以在<code>SimpleMemobj</code> <a href="https://www.gem5.org/_pages/static/scripts/part2/memoryobject/simple_memobj.cc">此处</a>下载实现。</p>
<p>下图显示了<code>CPUSidePort</code>，<code>MemSidePort</code>以及<code>SimpleMemobj</code>之间的关系。此图显示了对等端口如何与 <code>SimpleMemobj</code>的实现交互。 每个粗体功能都是我们必须实现的功能，非粗体功能是对等端口的端口接口。颜色突出显示通过对象的一个 API 路径（例如，接收请求或更新内存范围）。</p>
<p><img src="part2_5_memoryobject.assets/memobj_api.png" alt="SimpleMemobj 与其端口之间的交互" /></p>
<p>对于这个简单的内存对象，数据包只是从 CPU 端转发到内存端。但是，通过修改<code>handleRequest</code>和 <code>handleResponse</code>，我们可以创建丰富的功能对象，例如<a href="https://www.gem5.org/documentation/learning_gem5/part2/simplecache">下一章中</a>的缓存。</p>
<h3 id="创建配置文件"><a class="header" href="#创建配置文件">创建配置文件</a></h3>
<p>这是实现一个简单内存对象所需的所有代码！在<a href="https://www.gem5.org/documentation/learning_gem5/part2/simplecache">下一章中</a>，我们将以此为框架，并增加一些高速缓存逻辑，使这个内存对象到一个简单的缓存。但是，在此之前，让我们看一下将 SimpleMemobj 添加到系统的配置文件。</p>
<p>这个配置文件建立在<a href="https://www.gem5.org/documentation/learning_gem5/part1/simple_config">simple-config-chapter</a>中的<a href="https://www.gem5.org/documentation/learning_gem5/part1/simple_config">简单</a>配置文件之上 。然而，我们将实例化 <code>SimpleMemobj</code>并将其放置在 CPU 和内存总线之间，而不是将 CPU 直接连接到内存总线。</p>
<pre><code class="language-python">import m5
from m5.objects import *

system = System()
system.clk_domain = SrcClockDomain()
system.clk_domain.clock = '1GHz'
system.clk_domain.voltage_domain = VoltageDomain()
system.mem_mode = 'timing'
system.mem_ranges = [AddrRange('512MB')]

system.cpu = TimingSimpleCPU()

system.memobj = SimpleMemobj()

system.cpu.icache_port = system.memobj.inst_port
system.cpu.dcache_port = system.memobj.data_port

system.membus = SystemXBar()

system.memobj.mem_side = system.membus.slave

system.cpu.createInterruptController()
system.cpu.interrupts[0].pio = system.membus.master
system.cpu.interrupts[0].int_master = system.membus.slave
system.cpu.interrupts[0].int_slave = system.membus.master

system.mem_ctrl = DDR3_1600_8x8()
system.mem_ctrl.range = system.mem_ranges[0]
system.mem_ctrl.port = system.membus.master

system.system_port = system.membus.slave

process = Process()
process.cmd = ['tests/test-progs/hello/bin/x86/linux/hello']
system.cpu.workload = process
system.cpu.createThreads()

root = Root(full_system = False, system = system)
m5.instantiate()

print (&quot;Beginning simulation!&quot;)
exit_event = m5.simulate()
print('Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause()))
</code></pre>
<p>您可以<a href="https://www.gem5.org/_pages/static/scripts/part2/memoryobject/simple_memobj.py">在此处</a>下载此配置脚本 。</p>
<p>现在，当您运行此配置文件时，您将获得以下输出。</p>
<pre><code class="language-bash">gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled Jan  5 2017 13:40:18
gem5 started Jan  9 2017 10:17:17
gem5 executing on chinook, pid 5138
command line: build/X86/gem5.opt configs/learning_gem5/part2/simple_memobj.py

Global frequency set at 1000000000000 ticks per second
warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb.listener: listening for remote gdb #0 on port 7000
warn: CoherentXBar system.membus has no snooping ports attached!
warn: ClockedObject: More than one power state change request encountered within the same simulation tick
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
Hello world!
Exiting @ tick 507841000 because target called exit()
</code></pre>
<p>如果您使用<code>SimpleMemobj</code>调试标志运行，您可以看到所有来自 CPU 和发往 CPU 的内存请求和响应。</p>
<pre><code class="language-bash">gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled Jan  5 2017 13:40:18
gem5 started Jan  9 2017 10:18:51
gem5 executing on chinook, pid 5157
command line: build/X86/gem5.opt --debug-flags=SimpleMemobj configs/learning_gem5/part2/simple_memobj.py

Global frequency set at 1000000000000 ticks per second
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
      0: system.memobj: Got request for addr 0x190
  77000: system.memobj: Got response for addr 0x190
  77000: system.memobj: Got request for addr 0x190
 132000: system.memobj: Got response for addr 0x190
 132000: system.memobj: Got request for addr 0x190
 187000: system.memobj: Got response for addr 0x190
 187000: system.memobj: Got request for addr 0x94e30
 250000: system.memobj: Got response for addr 0x94e30
 250000: system.memobj: Got request for addr 0x190
 ...
</code></pre>
<p>您可能还想将 CPU 模型更改为乱序模型 ( <code>DerivO3CPU</code>)。使用乱序 CPU 时，您可能会看到不同的地址流，因为它允许一次处理多个内存请求。当使用乱序 CPU 时，现在会因为<code>SimpleMemobj</code>阻塞而出现很多停顿。</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../part2/part2_4_parameters.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>
                            <a rel="next" href="../part2/part2_6_simplecache.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../part2/part2_4_parameters.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    <a rel="next" href="../part2/part2_6_simplecache.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>

        <script type="text/javascript">
            window.playground_copyable = true;
        </script>
        <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
        <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->
    </body>
</html>
